// 路由器对象
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
import store from '@/store'

// 安装 Vue 插件
Vue.use(VueRouter)

// 解决重复调用报错的问题 https://github.com/vuejs/vue-router/issues/2881#issuecomment-520554378
// 缓存原本的 push 方法
const originalPush = VueRouter.prototype.push
// 缓存原本的 replace 方法
const originalReplace = VueRouter.prototype.replace

// 指定新的 push 方法
VueRouter.prototype.push = function (location, onResolve, onReject) {
  console.log(onResolve)
  console.log(onReject)
  // 如果指定了成功或者失败的回调,则直接调用原来的方法,不做任何处理
  if (onResolve || onReject) {
    // 不能直接调用, this 的指向不是 router 而是 undefined(打包遵循的是严格模式,否则的话就是 window)
    // originalPush(location, onResolve, onReject)

    // 指定 this 是 router 对象
    return originalPush.call(this, location, onResolve, onReject)
  }
  // 如果没有指定成功或失败的回调,则必须用 catch 来处理
  return originalPush.call(this, location).catch((err) => {
    // 如果是重复导航产生的错误,不再向外传递错误
    if (VueRouter.isNavigationFailure(err)) {
      // 产生的是成功的 Promise,成功的 value 是 err
      return err
    }
    // 如果是其他原因导致的错误,将错误向下传递
    return Promise.reject(err)
  })
}

// 指定新的 replace 方法
VueRouter.prototype.replace = function (location, onResolve, onReject) {
  // 如果指定了成功或者失败的回调,则直接调用原来的方法,不做任何处理
  if (onResolve || onReject) {
    // 不能直接调用, this 的指向不是 router 而是 undefined(打包遵循的是严格模式,否则的话就是 window)
    // originalReplace(location, onResolve, onReject)

    // 指定 this 是 router 对象
    return originalReplace.call(this, location, onResolve, onReject)
  }
  // 如果没有指定成功或失败的回调,则必须用 catch 来处理
  return originalReplace.call(this, location).catch((err) => {
    // 如果是重复导航产生的错误,不再向外传递错误
    if (VueRouter.isNavigationFailure(err)) {
      // 产生的是成功的 Promise,成功的 value 是 err
      return err
    }
    // 如果是其他原因导致的错误,将错误向下传递
    return Promise.reject(err)
  })
}

// 向外暴露路由器对象
const router = new VueRouter({
  // 模式
  mode: 'history', // 不带 #
  // 应用中的所有路由
  routes,
  scrollBehavior (to, from, savedPosition) {
    return { x: 0, y: 0 }
  }
})

/*
  导航守卫: 全局前置守卫
    to    代表准备去的地方的路由对象
    from  从哪个地方来的路由对象
    next  是一个函数
      next()      代表无条件放行
      next(false) 代表不放行,停在原地
      next('/')  next({path:'/'})  代表最终让它去哪
*/
// token 校验逻辑
router.beforeEach(async (to, from, next) => {
  // token 校验
  let token = store.state.user.token
  // 如果 token 存在,则代表登录了或者之前登录过
  if (token) {
    // 如果登录过了,又想去登录页,则直接跳转到首页
    if (to.path === '/login') {
      next('/')
    } else {
      // !! 将数值转换为布尔值, ! 取反, !! 获取到原来的值对应的布尔值
      let hasUserInfo = !!store.state.user.userInfo.nickName
      if (hasUserInfo) {
        // 用户信息存在,正常放行
        next()
      } else {
        // 用户信息不存在
        // 代表登录了,并且去的不是登录页,所以根据 token 发送请求获取用户真正的信息
        try {
          await store.dispatch('reqUserInfo')
          // 正常放行
          next()
        } catch (error) {
          // 如果发生错误,则清空 token 信息并跳转到登录页,让用户重新登录
          alert('用户的 toekn 过期或其他未知错误')

          store.dispatch('resetUserInfo')

          // 跳转到登录页,在用户输入用户名密码登录成功后,应该跳转到他本来想去的地方,而不是重新跳转到首页,所以这里需要将用户想去的地方使用参数进行传递,需要和登录逻辑去配合使用
          next('/login?redirect=' + to.path)
        }
      }
    }
  } else {
    // 代表用户没登录或者之前也没登录过
    // 必须登录后才能访问的多个界面使用全局守卫(交易相关、支付相关、用户中心相关)自动跳转前面想而没到的页面
    if (to.path.startsWith('/trade') || to.path.startsWith('/pay') || to.path.startsWith('/center')) {
      next('/login?redirect=' + to.path)
    } else {
      next()
    }
  }
})

export default router
